import type { AxiosInstance, AxiosRequestConfig } from "axios";
import axios from "axios";
import { ElLoading, ElMessage } from "element-plus";
import { reactive } from "vue";
import { useUserStore } from '@/stores/user'

const userStore = useUserStore();

/** 根据运行环境获取 基础请求 Url */
const getBaseUrl = () => {
    const value: string = import.meta.env.VITE_AXIOS_BASE_URL as string;
    return value;
}

/**
 * Loading 加载实例对象
 * 
 * 同时发起多个请求，只会显示一个 Loading
 */
const loadingInstance = reactive({
    target: {
        close: () => { }
    },   // loading 目标
    count: 0        // 请求数量
});

/**
 * 初始化 Axios
 * @param {AxiosRequestConfig} axiosConfig axios 配置
 * @param {Options} customOptions 自定义配置参数
 * @param {LoadingOptions} loadingOptions Loading 配置参数
 * @returns
 */
function createAxios(axiosConfig: AxiosRequestConfig, customOptions: Options = {}, loadingOptions: LoadingOptions = {}) {

    const axiosInstance = axios.create({
        baseURL: getBaseUrl(),
        timeout: 1000 * 10,
        responseType: 'json'
    });

    // 合并请求选项
    customOptions = Object.assign({
        /** 是否开启 Loading 加载效果。默认：关闭 */
        loading: false,
        /** 是否开启简洁的数据响应。默认：开启 */
        reductDataFormat: true,
        /** 是否开启接口错误信息提示。默认: 开启 */
        errorMessageShow: true,
        /** 是否开启 code 不为 0 时的信息提示。默认：关闭 */
        codeMessageShow: false
    }, customOptions);

    interceptorsRequest(axiosInstance, customOptions, loadingOptions);
    interceptorsResponse(axiosInstance, customOptions);

    return axiosInstance(axiosConfig);
}

export default createAxios;

/**
 * 请求拦截器
 * @param {AxiosInstance} axiosInstance axios 实例
 * @param {Options} customOptions 自定义配置参数
 * @param {LoadingOptions} loadingOptions Loading 加载层配置
 */
const interceptorsRequest = (axiosInstance: AxiosInstance, customOptions: Options = {}, loadingOptions: LoadingOptions = {}) => {
    axiosInstance.interceptors.request.use((config: any) => {
        // 在发送请求之前做些什么

        // 创建 Loading 实例
        if (customOptions.loading) {
            loadingInstance.count++;
            if (loadingInstance.count === 1) {
                loadingInstance.target = ElLoading.service(loadingOptions);
            }
        }

        // 自动携带 token 信息
        if (config.headers) {
            const token = userStore.user.accessToken;
            if (token) {
                config.headers.authToken = token;
            }
        }

        return config;
    }, (error: any) => {
        // 对请求错误做些什么
        return Promise.reject(error);
    })
}

/**
 * 响应拦截器
 * @param {AxiosInstance} axiosInstance axios 实例
 * @param {Options} customOptions 自定义配置参数
 */
const interceptorsResponse = (axiosInstance: AxiosInstance, customOptions: Options = {}) => {
    axiosInstance.interceptors.response.use((response: any) => {
        // 2xx 范围内的状态码都会触发该函数
        // 对响应数据做些什么

        customOptions.loading && closeLoading();

        // 接口返回状态代码不等于 0，提示接口返回的错误信息
        if (customOptions.codeMessageShow && response.data && response.data.code !== 0) {
            ElMessage({
                type: 'error',
                message: response.data.message || '操作失败'
            })
            // 具体逻辑不再需要执行
            return Promise.reject(response.data);
        }

        return customOptions.reductDataFormat ? response.data : response;
    }, (error: any) => {
        // 超过 2xx 范围的状态码都会触发该函数
        // 对响应错误做些什么

        customOptions.loading && closeLoading();
        customOptions.errorMessageShow && httpErrorStatusHandle(error);

        return Promise.reject(error);
    })
}

/**
 * 处理请求异常
 * @param error 
 */
const httpErrorStatusHandle = (error: any) => {
    let message = '接口请求出现异常';
    if (error && error.response) {
        switch (error.response.status) {
            case 403: message = '您没有权限操作！'; break;
            case 404: message = `请求地址不存在：${error.response.config.url}`; break;
            case 408: message = '请求超时，请稍后重试'; break;
            case 500: message = '服务器内部错误，技术人员正在处理'; break;
            default: message = '接口请求出现异常，请联系技术人员'; break;
        }
    }
    if (error.message) {
        if (error.message.includes('timeout')) {
            message = '请求超时，请稍后重试';
        }
        else if (error.message.includes('Network')) {
            message = window.navigator.onLine ? '服务器异常，请稍后重试' : '您断网了，请稍后重试';
        }
    }

    ElMessage({
        type: 'error',
        message
    });
}

/**
 * 关闭 Loading 层实例
 * 
 * 同时发起多个请求的情况下，
 * 只有在最后一个请求完成之后，才会关闭 Loading 加载层
 */
const closeLoading = () => {
    if (loadingInstance.count > 0) { loadingInstance.count--; }
    if (loadingInstance.count === 0) {
        loadingInstance.target.close();
        loadingInstance.target = {
            close: () => { }
        }
    }
}

/**
 * 自定义配置
 */
interface Options {
    /** 是否开启 Loading 加载效果。默认：关闭 */
    loading?: boolean
    /** 是否开启简洁的数据响应。默认：开启 */
    reductDataFormat?: boolean
    /** 是否开启接口错误信息提示。默认: 开启 */
    errorMessageShow?: boolean
    /** 是否开启 code 不为 0 时的信息提示。默认：关闭 */
    codeMessageShow?: boolean
}

/**
 * Loading 加载层 配置
 */
interface LoadingOptions {
    /** 显示在加载图标下方的加载文案 */
    text?: string
    /** 遮罩背景色 */
    background?: string
    /** Loading 的自定义类名 */
    customClass?: string
}